#include "DistributedPackets.h"
#include "../Solution/SolutionFactory.h"
#include "../Exception/GA_Exception.h"

void checkBufferConstraints(int curPos, int sizeIncrease, int maxSize) {
    if (curPos + sizeIncrease > maxSize) {
        throw Buffer_undersized_exception();
    }   
}

int DistributedPackets::packetToBytes(char buffer[], int bufferSize, const Packet &packet) {
    int bufferPos = 0;
    checkBufferConstraints(bufferPos, 2, bufferSize);
    
    buffer[bufferPos] = (char) packet.signal;
    bufferPos += 1;
    
    if ((packet.signal == ISSUE_JOB) || (packet.signal == JOB_SUBMISSION)) {
        buffer[bufferPos] = (char) packet.jobType;
        bufferPos += 1;
    }
    
    if (packet.signal == ISSUE_JOB) {
        checkBufferConstraints(bufferPos, sizeof(int), bufferSize);        
        int offset = bufferPos + sizeof(int);
        int solutionLength = SolutionFactory::serializeSolution(           
           ((char *) &buffer[offset]), 
           bufferSize - (offset), 
           packet.solPtr1
        );
           
        ((int *) &buffer[bufferPos])[0] = solutionLength;
        
        bufferPos += sizeof(int);
        bufferPos += solutionLength; 
    }
    
    if (packet.signal == JOB_SUBMISSION && packet.jobType == EVALUATE_FITNESS) {
        checkBufferConstraints(bufferPos, sizeof(double), bufferSize);
        ((double *) &buffer[bufferPos])[0] = packet.fitness;
        bufferPos += sizeof(double);
    }
    
    if (packet.signal == JOB_SUBMISSION && packet.jobType == CROSSOVER) {
        checkBufferConstraints(bufferPos, sizeof(int), bufferSize);        
        int offset = bufferPos + sizeof(int);
        int solutionLength = SolutionFactory::serializeSolution(           
           ((char *) &buffer[offset]), 
           bufferSize - (offset), 
           packet.solPtr1
        );
           
        ((int *) &buffer[bufferPos])[0] = solutionLength;
        
        bufferPos += sizeof(int);
        bufferPos += solutionLength;
        
        checkBufferConstraints(bufferPos, sizeof(int), bufferSize);        
        offset = bufferPos + sizeof(int);
        solutionLength = SolutionFactory::serializeSolution(           
           ((char *) &buffer[offset]), 
           bufferSize - (offset), 
           packet.solPtr2
        );
           
        ((int *) &buffer[bufferPos])[0] = solutionLength;
        
        bufferPos += sizeof(int);
        bufferPos += solutionLength;         
    }
    
    return bufferPos;
}

void DistributedPackets::bytesToPacket(const char buffer[], int bufferSize, Packet &packet) {
    int bufferPos = 0;
    
    checkBufferConstraints(bufferPos, 1, bufferSize);
    packet.signal = (Signal) buffer[bufferPos];
    bufferPos += 1;
    
    if ((packet.signal == ISSUE_JOB) || (packet.signal == JOB_SUBMISSION)) {
        checkBufferConstraints(bufferPos, 1, bufferSize);
        packet.jobType = (JobType) buffer[bufferPos];
        bufferPos += 1;
    }
    
    if (packet.signal == ISSUE_JOB) {
        checkBufferConstraints(bufferPos, sizeof(int), bufferSize);
        int solutionLength = ((int *) &buffer[bufferPos])[0];
        checkBufferConstraints(bufferPos, solutionLength, bufferSize);
        bufferPos += sizeof(int);
        packet.solPtr1 = SolutionFactory::deserializeSolution(&buffer[bufferPos], solutionLength);
        bufferPos += solutionLength; 
    }
    
    if (packet.signal == JOB_SUBMISSION && packet.jobType == EVALUATE_FITNESS) {
        checkBufferConstraints(bufferPos, sizeof(double), bufferSize);
        packet.fitness = ((double *) &buffer[bufferPos])[0];
        bufferPos += sizeof(double);
    }
    
    if (packet.signal == JOB_SUBMISSION && packet.jobType == CROSSOVER) {
        int solutionLength;
        checkBufferConstraints(bufferPos, sizeof(int), bufferSize);
        solutionLength = ((int *) &buffer[bufferPos])[0];
        checkBufferConstraints(bufferPos, solutionLength, bufferSize);
        bufferPos += sizeof(int);
        packet.solPtr1 = SolutionFactory::deserializeSolution(&buffer[bufferPos], solutionLength);
        bufferPos += solutionLength;
        
        checkBufferConstraints(bufferPos, sizeof(int), bufferSize);
        solutionLength = ((int *) &buffer[bufferPos])[0];
        checkBufferConstraints(bufferPos, solutionLength, bufferSize);
        bufferPos += sizeof(int);
        packet.solPtr2 = SolutionFactory::deserializeSolution(&buffer[bufferPos], solutionLength);
        bufferPos += solutionLength;        
    }
}
